home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1989 Paul Pomes
- * Copyright (c) 1989 University of Illinois Board of Trustees
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of Illinois, Urbana. In addition, redistribution
- * and use must conform to the terms listed in the CopyLeft text below.
- *
- * The name of the University may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #ifndef lint
- static char rcsid[] = "@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.25 1991/05/10 02:21:27 paul Exp paul $";
- #endif /* lint */
-
- #include <stdio.h>
- #include <assert.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #if defined(pyr) || defined(is68k) || defined(NeXT) || defined(__convex__) \
- || defined(BSD4_4) || defined(ibm032)
- # include <sys/time.h>
- # include <sys/vnode.h>
- # define IREAD VREAD
- # define IWRITE VWRITE
- #else /* ! pyr && ! is68k */
- # if defined(sun) || defined(convex) || defined(apollo)
- # include <sys/stat.h>
- # define IREAD S_IREAD
- # define IWRITE S_IWRITE
- # else /* ! sun && ! convex */
- # include <sys/inode.h>
- # endif /* sun || convex */
- #endif /* pyr || is68k */
- #include <netdb.h>
- #include <ctype.h>
- #include <sys/socket.h>
- #include <sys/syslog.h>
- #include <sys/errno.h>
- #include <sys/wait.h>
- #include <netinet/in.h>
- #include <sysexits.h>
- #include <strings.h>
- #include "phquery.h"
- #include "messages.h"
-
- #define VERSION "3.8"
-
- /* Domain to append to ph aliases when creating Reply-To: fields */
- #ifndef DOMAIN
- # define DOMAIN "uiuc.edu"
- #endif /* DOMAIN */
-
- /* Designated server port */
- #ifndef QISERVICE
- # define QISERVICE "ns"
- #endif /* QISERVICE */
-
- /* Mail transport agent of choice */
- #if defined(BSD4_4)
- #define SENDMAIL "/usr/sbin/sendmail"
- #else /* !BSD4_4 */
- #define SENDMAIL "/usr/lib/sendmail"
- #endif /* BSD4_4 */
-
- /* How to print/log error messages */
- #define DANGER_WILL_ROBINSON(KateBush) \
- { if (Debug) \
- perror (KateBush); \
- if (Log) \
- syslog (LOG_ERR, strcat (KateBush, ": %m")); \
- finis (); }
-
- /*
- ** PHQUERY -- Resolve fuzzy addresses to specific a user@FQDN
- **
- ** FQDN := Fully Qualified Domain Name
- ** Phquery is invoked as a mailer (not a final mailer!) by sendmail
- ** to resolve addresses of the form user@DOMAINMASTER where DOMAINMASTER
- ** is a m4 define used in building an IDA sendmail.cf file. At UIUC
- ** this would be user@uiuc.edu . The user token is interpreted first
- ** as a QI alias, then as a full name if that fails. QI is the CSnet
- ** Query Interpreter. At UIUC it contains the entire campus phone
- ** directory plus the unit directory. A user entry has about as many
- ** fields as ls has option letters. The most important are alias, name,
- ** email, phone, department, and curriculum. In the simplest case,
- ** matching an alias (guaranteed unique) returns the email address.
- **
- ** Since life is seldom simple, the alternate cases/actions are summarized
- **
- ** a) alias match, email found
- ** write a X-PH-To: header with the email address found, copy the
- ** rest of the message, and re-invoke sendmail
- ** OR
- ** write a X-PH: VX.Y@<host> and re-invoke sendmail. This is
- ** useful for sites that don't wish to expand alias lists in the
- ** header block.
- ** b) alias match, no email field:
- ** return public fields of ph entry and suggest phone usage
- ** c) alias match, bogus email field:
- ** sendmail catches this one. The user will see the X-PH-To:
- ** header. Not the best so far.....
- ** d) alias fail:
- ** try name field
- ** e) single name match, email present:
- ** deliver as in a)
- ** f) single name match, no email field:
- ** handle as in b)
- ** g) single name match, bogus email field:
- ** handle as in c)
- ** h) multiple (<5) name matches:
- ** return alias, name, email, and dept fields of matches
- ** i) multiple (>5) name matches:
- ** return "too ambiguous" message
- **
- ** Phquery is also used to create return addresses of the form
- ** ph-alias@DOMAINMASTER. This is implemented by adding the fields
- **
- ** Resent-From: postmaster@<host>
- ** Reply-To: ph-alias@DOMAINMASTER
- ** Comment: Reply-To: added by phquery (Vx.y)
- **
- ** N.B., RFC-822, Section 4.4.1 requires that the From / Resent-From
- ** fields be a single, authenticated machine address.
- */
-
- /* some handy defines */
- #define CHNULL ('\0')
- #define CPNULL ((char *) NULL)
- #define FILE_NULL ((FILE *) NULL)
- #define NADD_NULL ((struct NewAddress *) NULL)
- #define QIR_NULL ((struct QI_response *) NULL)
-
- /* some handy compare operators */
- #define nequal(s1,s2,n) (strncasecmp (s1, s2, n) == 0)
- #define equal(s1,s2) (strcasecmp (s1, s2) == 0)
-
- /* large string size */
- #define MAXSTR 250
-
- /* Bit flags to control printing of informative messages in ErrorReturn() */
- #define NO_MATCH_MSG 0x1
- #define MULTI_MSG 0x2
- #define ABSENT_MSG 0x4
- #define TOO_MANY_MSG 0x8
- #define PHONE_MSG 0x10
-
- FILE *ToQI = FILE_NULL; /* write to the QI */
- FILE *FromQI = FILE_NULL; /* read from the QI */
-
- extern int errno;
-
- /* Set to carbon-copy postmaster on error returns */
- int PostmasterCC = 0;
-
- /* Set if the reply-to: field on outgoing mail is to inserted */
- int ReplyTo = 0;
-
- /* Hostname of this machine */
- char HostNameBuf[100];
-
- /* How program was invoked (argv[0]) for error messages */
- char *MyName;
-
- /* Exit status for finis() reporting to calling process */
- int ExitStat = EX_TEMPFAIL;
-
- /* Temporary message file */
- char TmpFile[] = "/tmp/PhMailXXXXXXX";
-
- /* Temporary file for creating error messages */
- char ErrorFile[] = "/tmp/PhErrMailXXXXXXX";
-
- /* Temporary file for rewriting messages */
- char NewFile[] = "/tmp/PhNewMailXXXXXXX";
-
- /*
- * The types of nameserver queries to make.
- * N.B., Query() assumes that "name" is the last token in this list.
- * Also be sure to duplicate any extra keywords added to TryList to the
- * query fprintf near the top of Query().
- */
- char *TryList[] = { "alias", "callsign", "name", CPNULL };
-
- /*
- * How to report events: Debug set for stderr messages, Log for syslog.
- * Setting Debug disables fork/execve in ReMail.
- */
- int Debug = 0;
- int Log = 1;
-
- /* From address supplied by caller */
- char *From = CPNULL;
-
- char *usage[] = {
- "usage: %s [-d] [-p] [-s] [-l] [-R] [-i] [-x service] [-f FromAddress] address1 [address2]",
- CPNULL
- };
-
- #ifdef __STDC__
- # include <unistd.h>
- # include <stdlib.h>
- void ErrorReturn(NADD *, FILE *, char *[]);
- void FindFrom(FILE *);
- void ReMail(NADD *, FILE *, char *[]);
- char * CodeString(int);
- FILE * OpenTemp(const char *);
- QIR * PickField (QIR *, int);
- void Query(NADD *);
- int SendQuery(NADD *, const char *, const char *);
- void RevQuery(NADD *);
- QIR * ReadQI(FILE *);
- int FieldValue(const char *);
- void GarbageCollect(QIR *);
- char * Malloc(unsigned int);
- void PrintMsg(FILE *, char *[]);
- char * Realloc(char *, unsigned int);
- void PrtUsage(int);
- void finis();
- #else /* !__STDC__ */
- # define const
- void ErrorReturn();
- void FindFrom();
- void ReMail();
- char * CodeString();
- FILE * OpenTemp();
- QIR * PickField ();
- void Query();
- int SendQuery();
- void RevQuery();
- QIR * ReadQI();
- int FieldValue();
- void GarbageCollect();
- char * Malloc();
- void PrintMsg();
- char * Realloc();
- void PrtUsage();
- void finis();
- #endif /* !__STDC__ */
- void ContactQI();
-
- char *CopyLeft[] = {
- " Written by Paul Pomes, University of Illinois, Computing Services Office",
- " Copyright (C) 1989 by Paul Pomes and the University of Illinois Board",
- " of Trustees",
- " ",
- " This program is distributed in the hope that it will be useful, but without",
- " any warranty. No author or distributor accepts responsibility to anyone",
- " for the consequences of using it, no matter how awful, or for whether it",
- " serves any particular purpose or works at all, unless s/he says so in",
- " writing.",
- " ",
- " Everyone is granted permission to copy, modify and redistribute this",
- " program under the following conditions:",
- " ",
- " Permission is granted to anyone to make or distribute copies of program",
- " source code, either as received or modified, in any medium, provided",
- " that all copyright notices, permission and nonwarranty notices are",
- " preserved, and that the distributor grants the recipient permission for",
- " further redistribution as permitted by this document, and gives him and",
- " points out to him an exact copy of this document to inform him of his",
- " rights.",
- " ",
- " Permission is granted to distribute this program in compiled or",
- " executable form under the same conditions applying for source code,",
- " provided that either",
- " ",
- " A. it is accompanied by the corresponding machine-readable source code,",
- " or",
- " B. it is accompanied by a written offer, with no time limit, to give",
- " anyone a machine-readable copy of the corresponding source code in",
- " return for reimbursement of the cost of distribution. This written",
- " offer must permit verbatim duplication by anyone.",
- " C. it is distributed by someone who received only the executable form,",
- " and is accompanied by a copy of the written offer of source code",
- " which he received along with it.",
- " ",
- " In other words, you are welcome to use, share and improve this program.",
- " You are forbidden to forbid anyone else to use, share and improve what",
- " you give them. Help stamp out software-hoarding!",
- " ",
- "UUCP: {att,iuvax,uunet}!uiucuxc!paul ICBM: 40 06 47 N / 88 13 35 W",
- "Internet, BITNET: paul@uxc.cso.uiuc.edu Phone: 217 333 6262",
- "US Mail: UofIllinois, CSO, 1304 W Springfield Ave, Urbana, IL 61801-2910",
- CPNULL
- };
-
- main(argc, argv, envp)
- int argc;
- char *argv[], *envp[];
- {
- extern int optind; /* from getopt () */
- extern char *optarg; /* from getopt () */
- int option; /* option "letter" */
- int i; /* good ol' i */
- char *Service = CPNULL; /* ph alias from -x */
- FILE *Msg; /* stream pointer for temp file */
- NADD *New, *NewP; /* translated addresses */
- char Buf[MAXSTR];
- extern char HostNameBuf[];
-
- MyName = ((MyName = rindex (*argv, '/')) == CPNULL)
- ? *argv : (MyName + 1);
-
- while ((option = getopt (argc, argv, "f:r:x:pRsdli")) != EOF) {
- switch (option) {
- case 'f':
- From = optarg;
- break;
-
- case 'x':
- Service = optarg;
- break;
-
- case 'R':
- /* Re-write outgoing address with Reply-To: field */
- ReplyTo++;
- break;
-
- case 's':
- /* Designated humor section for humor-less CSO types */
- if (Debug) {
- fprintf (stderr, "Checking Figure 1 ......");
- (void) fflush (stderr);
- sleep (2);
- fprintf (stderr, "done.\n");
- }
- break;
-
- case 'r':
- From = optarg;
- break;
-
- case 'p':
- PostmasterCC++;
- break;
-
- case 'l':
- Log++;
- break;
-
- case 'd':
- Debug++;
- if (Debug == 1)
- PrtUsage (1);
- Log = 0;
- break;
-
- case 'i':
- PrtUsage (1);
- finis ();
- break;
-
- default:
- PrtUsage (0);
- finis ();
- break;
- }
- }
- argc -= optind; /* skip options */
- argv += optind;
-
- /* Fire up logging, or not, as the flags may be */
- if (Log)
- #ifdef LOG_MAIL
- # ifndef SYSLOG
- # define SYSLOG LOG_MAIL
- # endif
- openlog(MyName, LOG_PID, SYSLOG);
- #else
- openlog(MyName, LOG_PID);
- #endif
-
- if (Log)
- syslog (LOG_DEBUG, "From %s", From);
-
- /* fetch our host name, some use will be found for it.... */
- if (gethostname (HostNameBuf, 100-1) != 0)
- DANGER_WILL_ROBINSON("gethostname")
-
- /* Open the temp file, copy the message into it */
- if ((Msg = OpenTemp (TmpFile)) == FILE_NULL)
- finis ();
- while ((i = fread (Buf, sizeof (char), MAXSTR, stdin)) != 0)
- if (fwrite (Buf, sizeof (char), i, Msg) != i)
- DANGER_WILL_ROBINSON("Msg copy")
- (void) fflush (Msg);
-
- /*
- * Remaining arguments are addresses. If From == CHNULL,
- * then submission was done locally and return address has
- * to be on the From: line.
- */
- if (From == CPNULL || (From != CPNULL && From == CHNULL))
- FindFrom (Msg);
-
- if (ReplyTo) {
-
- /*
- * Check with QI to see if this person has a email entry.
- * If so add the Resent-From, Reply-To, and Comment fields.
- * Then invoke ReMail with xyzzy appended to the From address
- * so that sendmail won't send it back to us. If a
- * Reply-To: field is already present, handle as though no
- * email field was found.
- */
-
- /*
- * Allocate NewAddress structs for from address, to addresses,
- * plus 1 for terminal null.
- */
- New = (NADD *) Malloc ((unsigned) ((argc+2) * sizeof (NADD)));
- (New + argc + 1)->original = CPNULL;
- NewP = New;
- RevQuery (NewP);
- assert (NewP->new != CPNULL);
-
- /* If a single alias was found, append the domain */
- if (abs (NewP->code) == LR_OK) {
- NewP->new =
- Realloc (NewP->new, (unsigned) (strlen (NewP->new)
- + strlen (DOMAIN) + 2));
- (void) strcat (NewP->new, "@");
- (void) strcat (NewP->new, DOMAIN);
- }
-
- /* Add To: addresses to NewP array */
- NewP++;
- while (argc > 0) {
- NewP->original = *argv;
- NewP->new = CPNULL;
- NewP++; argv++; argc--;
- }
-
- /* ReMail will add the new headers and call sendmail */
- ReMail (New, Msg, envp);
-
- /* We done good. */
- ExitStat = EX_OK;
- finis ();
- }
-
- /*
- * If not a ReplyTo ...
- * Allocate NewAddress structs for addresses (or just one if this
- * is a service forward.
- */
- i = (Service == CPNULL) ? argc : 1;
- New = (NADD *) Malloc ((unsigned) ((i+1) * sizeof (NADD)));
- (New + i)->original = CPNULL;
- NewP = New;
-
- if (Service != CPNULL) {
- NewP->original = Service;
- NewP->new = CPNULL;
- Query (NewP);
- assert (NewP->new != CPNULL);
- if (Debug)
- printf ("code %d, %s --> %s\n",
- NewP->code, NewP->original, NewP->new);
- if (Log)
- syslog (LOG_INFO, "%s --> %s",
- NewP->original, NewP->new);
- }
- else
- /* Loop on addresses in argv building up translation table */
- while (argc > 0) {
- NewP->original = *argv;
- NewP->new = CPNULL;
- Query (NewP);
- assert (NewP->new != CPNULL);
- if (Debug)
- printf ("code %d, %s --> %s\n",
- NewP->code, NewP->original, NewP->new);
- if (Log)
- syslog (LOG_INFO, "%s --> %s",
- NewP->original, NewP->new);
- NewP++; argv++; argc--;
- }
-
- /*
- * Now re-invoke sendmail with the translated addresses.
- * Make one pass for collecting error returns into one message.
- */
- for (NewP = New; NewP->original != CPNULL; NewP++)
- if (abs (NewP->code) != LR_OK) {
- ErrorReturn (NewP, Msg, envp);
- break;
- }
-
- /* Any good addresses? */
- for (NewP = New; NewP->original != CPNULL; NewP++)
- if (abs (NewP->code) == LR_OK) {
- ReMail (NewP, Msg, envp);
- break;
- }
-
- /* exit */
- ExitStat = EX_OK;
- finis ();
- }
- /*
- ** ContactQI -- Connect to the QI server
- **
- ** Examine the ToQI and FromQI file descriptors. If NULL, open
- ** socket connections to the QI server. Exits on any error.
- **
- ** Parameters:
- ** none
- **
- ** Returns:
- ** None
- **
- ** Side Effects:
- ** Changes ToQI and FromQI if an open is done.
- */
-
- void
- ContactQI ()
- {
- int sock; /* our socket */
- struct sockaddr_in QI; /* the address of the nameserver */
- struct servent *Ns; /* nameserver service entry */
- struct hostent *Host; /* host entry for nameserver */
- char *QiHost = QI_HOST; /* Initial Qi server */
- extern FILE *ToQI, *FromQI; /* read/write streams to QI */
-
- /* Already opened... */
- if (ToQI != FILE_NULL && FromQI != FILE_NULL) {
- if (Debug)
- printf("ToQI/FromQI already opened\n");
- return;
- }
- if (Debug)
- printf("opening ToQI/FromQI\n");
-
- /* Locate the proper port */
- if (Ns = getservbyname (QISERVICE, "tcp")) {
- QI.sin_port = Ns->s_port;
- } else {
- if (Debug)
- fprintf (stderr, "server \"%s\" unknown - using 105", QISERVICE);
- if (Log)
- syslog (LOG_ERR, "server \"%s\" unknown - using 105", QISERVICE);
- QI.sin_port = 105;
- }
- QI.sin_family = AF_INET;
-
- again:
- /* Get a socket for the QI connection */
- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- if (Log)
- syslog (LOG_ERR, "ContactQI: socket(): %m");
- if (Debug)
- fprintf (stderr, "ContactQI: can't create socket");
- finis();
- }
-
- /* Locate the proper host */
- if (Host = gethostbyname (QiHost)) {
- bcopy (Host->h_addr, (char *) &QI.sin_addr.s_addr, 4);
- } else {
- if (Log)
- syslog (LOG_ERR, "ContactQI: gethostbyname(%s): %m", QiHost);
- if (Debug) {
- fprintf (stderr, "gethostbyname(%s):", QiHost);
- perror ("");
- }
- finis();
- }
-
- /* Connect to the nameserver */
- if (connect (sock, (struct sockaddr *) &QI, sizeof (QI)) < 0) {
- if (Log)
- syslog (LOG_INFO, "ContactQI: connect(%s): %m", QiHost);
- if (Debug) {
- fprintf (stderr, "ContactQI: connect(%s):", QiHost);
- perror ("");
- }
- (void) close(sock);
- #ifdef QI_ALT
- if (!equal (QiHost, QI_ALT)) {
- QiHost = QI_ALT;
- goto again;
- }
- #endif /* QI_ALT */
- finis ();
- }
-
- /* Connection ok, change to canonical form */
- ToQI = fdopen (sock, "w");
- FromQI = fdopen (sock, "r");
- return;
- }
- /*
- ** ErrorReturn -- Create and send informative mail messages
- **
- ** The envelope from address should be set to null as per RFC-821
- ** in regard to notification messages (Section 3.6).
- **
- ** Parameters:
- ** Addr -- pointer to NewAddress structure with addresses
- ** and messages
- ** Omsg -- stream pointer to original message
- ** envp -- environment pointer for fork/execve
- **
- ** Returns:
- ** Nothing
- **
- ** Side Effects:
- ** None
- */
-
- char *ap[] = { "-sendmail", "-f", "MAILER-DAEMON", "-t", 0};
-
- void
- ErrorReturn (Addr, Omsg, envp)
- NADD *Addr;
- FILE *Omsg;
- char *envp[];
- {
- int i; /* Good ol' i */
- char Buf[MAXSTR]; /* Temp for copying msg test */
- FILE *Emsg; /* For creating the error msg */
- int pid; /* For fork() */
- int flags = 0; /* Controls printing of msgs */
- int SubCode; /* Printing control */
- NADD *AddrP; /* Loop variable */
- QIR *QIp; /* Another loop variable */
- extern char *ap[];
-
- /* Open the error file */
- if ((Emsg = OpenTemp (ErrorFile)) == FILE_NULL)
- finis ();
-
- /* Insert the headers */
- if (fprintf (Emsg, "To: %s\n", From) < 0)
- finis ();
- if (PostmasterCC)
- fprintf (Emsg, "Cc: Postmaster\n");
- fprintf (Emsg, "Subject: Returned mail - nameserver error report\n\n");
- fprintf (Emsg, " --------Message not delivered to the following:\n\n");
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) != LR_OK)
- fprintf (Emsg, " %15s %s\n", AddrP->original, AddrP->new);
- fprintf (Emsg, "\n --------Error Detail (phquery V%s):\n\n", VERSION);
-
- /* Loop again to insert messages */
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_NOMATCH) {
- if (! (flags & NO_MATCH_MSG)) {
- PrintMsg (Emsg, NoMatchMsg);
- flags |= NO_MATCH_MSG;
- break;
- }
- }
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_ABSENT) {
- if (! (flags & ABSENT_MSG)) {
- PrintMsg (Emsg, AbsentMsg);
- flags |= ABSENT_MSG;
- if (! (flags & PHONE_MSG)) {
- PrintMsg (Emsg, PhoneMsg);
- flags |= PHONE_MSG;
- }
- }
- for (QIp = AddrP->QIalt; QIp->code < 0; QIp++)
- if (abs (QIp->code) == LR_OK)
- fprintf (Emsg, " %s: %s\n",
- Fields[QIp->field].value, QIp->message);
- (void) putc ('\n', Emsg);
- }
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_TOOMANY) {
- if (! (flags & TOO_MANY_MSG)) {
- PrintMsg (Emsg, TooManyMsg);
- flags |= TOO_MANY_MSG;
- break;
- }
- }
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_AMBIGUOUS) {
- if (! (flags & MULTI_MSG)) {
- PrintMsg (Emsg, MultiMsg);
- flags |= MULTI_MSG;
- if (! (flags & PHONE_MSG)) {
- PrintMsg (Emsg, PhoneMsg);
- flags |= PHONE_MSG;
- }
- }
- for (QIp = AddrP->QIalt, SubCode = QIp->subcode;
- QIp->code < 0; QIp++) {
- if (QIp->subcode != SubCode) {
- SubCode = QIp->subcode;
- (void) putc ('\n', Emsg);
- }
- if (abs (QIp->code) == LR_OK)
- fprintf (Emsg, " %s: %s\n",
- Fields[QIp->field].value, QIp->message);
- }
- (void) putc ('\n', Emsg);
- }
- fprintf (Emsg, "\n --------Unsent Message below:\n\n");
- rewind (Omsg);
- while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0) {
- if (fwrite (Buf, sizeof (char), i, Emsg) != i)
- DANGER_WILL_ROBINSON("ErrorReturn: Emsg copy")
- }
- fprintf (Emsg, "\n --------End of Unsent Message\n");
- (void) fflush (Emsg);
- (void) fclose (Emsg);
- if (freopen (ErrorFile, "r", stdin) == FILE_NULL)
- DANGER_WILL_ROBINSON("ErrorReturn: ErrorFile freopen")
-
- /* Zap file so it disappears automagically */
- if (! Debug)
- (void) unlink (ErrorFile);
-
- /*
- * fork, then execve sendmail for delivery
- */
-
- pid = 0;
- if (! Debug && (pid = fork ()) == -1)
- DANGER_WILL_ROBINSON("ErrorReturn: fork")
- if (pid) {
- (void) wait(0);
- return;
- }
- else if (! Debug)
- execve (SENDMAIL, ap, envp);
- }
- /*
- ** FindFrom -- Find From: address in message headers
- **
- ** Parameters:
- ** MsgFile -- stream pointer to message
- **
- ** Returns:
- ** Nothing
- **
- ** Side Effects:
- ** Global From pointer is adjusted to point at either a
- ** malloc'ed area containing the address, or to the
- ** constant string "Postmaster" if none is found.
- */
-
- void
- FindFrom (MsgFile)
- FILE *MsgFile;
- {
- char *p1, *p2;
- extern char *From;
- char Buf[MAXSTR];
-
- rewind (MsgFile);
- while (fgets (Buf, MAXSTR, MsgFile) != CPNULL && *Buf != '\n') {
- if (strncasecmp (Buf, "From:", 5))
- continue;
- else {
- if ((p1 = index (Buf, '<')) != CPNULL) {
- p1++;
- if ((p2 = index (Buf, '>')) != CPNULL) {
- From = Malloc ((unsigned) ((p2-p1)+1));
- (void) strncpy (From, p1, (p2-p1));
- }
- else {
- if (Debug)
- fprintf (stderr, "Unbalanced <> in From: address\n");
- if (Log)
- syslog (LOG_ERR, "Unbalanced <> in From: address\n");
- From = "Postmaster";
- }
- }
- else {
- /*
- * Punt to postmaster. If there's too
- * many, I'll fix this someday.
- */
- if (Debug)
- fprintf (stderr, "No <> in From: address\n");
- if (Log)
- syslog (LOG_ERR, "No <> in From: address\n");
- From = "Postmaster";
- }
- break;
- }
- }
- if (From == CPNULL) {
- if (Debug)
- fprintf (stderr, "No From: address in message\n");
- if (Log)
- syslog (LOG_ERR, "No From: address in message\n");
- From = "Postmaster";
- }
- }
- /*
- ** ReMail -- Forward message to recipients after adding phquery headers
- **
- ** Parameters:
- ** Addr -- pointer to NewAddress structure with addresses
- ** and messages
- ** Omsg -- stream pointer to original message
- ** envp -- environment pointer for fork/execve
- **
- ** Returns:
- ** Nothing
- **
- ** Side Effects:
- ** None
- */
-
- void
- ReMail (Addr, Omsg, envp)
- NADD *Addr;
- FILE *Omsg;
- char *envp[];
- {
- int napi = 0;
- int i;
- char Buf[MAXSTR];
- NADD *AddrP;
- FILE *Nmsg;
- int pid = 0;
- char *nap[50], nFrom[100];
- extern char *From, HostNameBuf[];
- extern int ReplyTo;
-
- /* Open the rewrite file */
- if ((Nmsg = OpenTemp (NewFile)) == FILE_NULL)
- finis ();
-
- /* Fill out the first portion of the sendmail argument vector */
- nap[napi++] = "-sendmail";
- nap[napi++] = "-f";
- if (ReplyTo == 0)
- nap[napi++] = From;
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_OK)
- nap[napi++] = AddrP->new;
- else {
- /*
- * Tack on .xyzzy to the From address so sendmail will know
- * it's been here.
- */
- (void) strcpy (nFrom, From);
- (void) strcat (nFrom, ".xyzzy");
- nap[napi++] = nFrom;
- }
-
- /* Read and copy the header block, adding X-PH-To: or X-PH: header */
- rewind (Omsg);
- while (fgets (Buf, MAXSTR, Omsg) != CPNULL && *Buf != '\n') {
- if ((nequal (Buf, "To:", 3) || nequal (Buf, "Cc:", 3)
- || nequal (Buf, "From:", 5)) && pid == 0) {
- int LineLength;
-
- if (ReplyTo == 0) {
-
- /* Write the PH header and add to argv */
- #ifdef EXPAND_TO
- if (fprintf (Nmsg, "X-PH(%s)-To:", VERSION) < 0)
- finis ();
- LineLength = 8;
- for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
- if (abs (AddrP->code) == LR_OK) {
- if ((LineLength + strlen (AddrP->new)) > 75) {
- fprintf (Nmsg, "\n\t");
- LineLength = 8;
- }
- fprintf (Nmsg, " %s", AddrP->new);
- }
- (void) putc ('\n', Nmsg);
- #else /* ! EXPAND_TO */
- fprintf (Nmsg, "X-PH: V%s@%s\n", VERSION, HostNameBuf);
- #endif /* EXPAND_TO */
- pid++;
- }
- else if (ReplyTo == 1) {
-
- /* Add the Reply-To: fields */
- AddrP = Addr;
- if (fprintf (Nmsg, "Comment: Reply-To: added by phquery (V%s)\n", VERSION) < 0)
- finis ();
- fprintf (Nmsg, "Resent-From: postmaster@%s\n", HostNameBuf);
- fprintf (Nmsg, "Reply-To: %s\n", AddrP->new);
- AddrP++;
- for (; AddrP->original != CPNULL; AddrP++)
- nap[napi++] = AddrP->original;
- pid++;
- }
- }
- fputs (Buf, Nmsg);
- }
- (void) fputs (Buf, Nmsg);
- nap[napi] = CPNULL;
-
- if (Debug) {
- printf ("Final send vector:");
- for (i = 0; nap[i] != CPNULL; i++)
- printf (" %s", nap[i]);
- (void) putchar ('\n');
- }
-
- /* Copy the remainder of the message */
- while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0)
- if (fwrite (Buf, sizeof (char), i, Nmsg) != i)
- DANGER_WILL_ROBINSON("ReMail: nmsg copy")
-
- /* Re-arrange the stream pointers and invoke sendmail */
- (void) fflush (Nmsg);
- (void) fclose (Nmsg);
- if (freopen (NewFile, "r", stdin) == FILE_NULL)
- DANGER_WILL_ROBINSON("ReMail: NewFile freopen")
-
- /* Zap file so it disappears automagically */
- if (! Debug)
- (void) unlink (NewFile);
-
- /*
- * fork, then execve sendmail for delivery
- */
-
- pid = 0;
- if (! Debug && (pid = fork ()) == -1)
- DANGER_WILL_ROBINSON("ReMail: fork")
- if (pid) {
- (void) wait(0);
- return;
- }
- else if (! Debug)
- execve (SENDMAIL, nap, envp);
- }
- /*
- ** CodeString -- Return text string corresponding to supplied reply code
- **
- ** Parameters:
- ** code -- reply value
- **
- ** Returns:
- ** char pointer to text string or NULL pointer if no matching
- ** key is located.
- **
- ** Side Effects:
- ** None
- */
-
- char *
- CodeString (code)
- int code;
- {
- struct ReplyCodes *Cpnt;
- extern struct ReplyCodes Codes[];
-
- for (Cpnt = Codes; Cpnt->key != -1; Cpnt++)
- if (Cpnt->key == abs (code))
- return (Cpnt->value);
- return (CPNULL);
- }
- /*
- ** OpenTemp -- Create and open a temporary file
- **
- ** For the supplied file name, create, open, and chmod the file
- **
- ** Parameters:
- ** Name -- pathname of file to create in mkstemp format
- **
- ** Returns:
- ** Stream descriptor of resulting file, or NULL if error
- **
- ** Side Effects:
- ** mkstemp modifies calling argument
- */
-
- FILE *
- OpenTemp (Name)
- const char *Name;
- {
- int fd;
- FILE *Stream;
-
- if ((fd = mkstemp (Name)) == -1)
- DANGER_WILL_ROBINSON("OpenTemp: mkstemp")
-
- /* Protect it */
- if (fchmod (fd, IREAD|IWRITE) == -1)
- DANGER_WILL_ROBINSON("OpenTemp: fchmod")
-
- /* Make fd a stream */
- if ((Stream = fdopen (fd, "r+")) == FILE_NULL)
- DANGER_WILL_ROBINSON("OpenTemp: fdopen")
- return (Stream);
- }
- /*
- ** PickField -- Find the QI_response with the named field
- **
- ** Cycle through a chain of QI_response's looking for one with the
- ** named field. Return a pointer to that one or NULL if not present.
- ** Assumes that the last QI_response.code > 0.
- **
- ** Parameters:
- ** qp -- QI_response chain pointer
- ** field -- QI field to search for
- **
- ** Returns:
- ** pointer to located QI_response or NULL if not found
- **
- ** Side Effects:
- ** None
- */
-
- QIR *
- PickField (qp, field)
- QIR *qp;
- int field;
- {
- do {
- if (qp->field == field)
- return (qp);
- } while ((qp++)->code < 0);
- return (QIR_NULL);
- }
- /*
- ** Query -- Create queries to send to the CSnet central server
- **
- ** Using the alias, call-sign, and full name fields, as known by the
- ** CSnet central name server Query Interpreter, Query creates variants
- ** of the supplied name (New->original) if a straight alias lookup fails.
- ** For each variant, SendQuery() is called until either one succeeds or
- ** all variants are exhausted.
- **
- ** Parameters:
- ** New -- pointer to NewAddress struct
- **
- ** Returns:
- ** None
- **
- ** Side Effects:
- ** Modifies contents under New pointer.
- */
-
- void
- Query(New)
- NADD *New;
- {
- char scratch[MAXSTR]; /* copy of FullName w.o. punct */
- char *sp; /* work ptrs for scratch */
- #ifdef WILDNAMES
- char *sp2; /* work ptrs for scratch */
- #endif /* WILDNAMES */
- char **Lpnt = TryList; /* Loop pointer for TryList */
- int NoMore = -1; /* set if all name variants done */
-
- /*
- * Try the query as an alias lookup first, then as a full name lookup.
- */
-
- do {
- /*
- * Convert punctuation/separators in scratch to space
- * characters one at a time if testing for name. If
- * WILDNAMES is #define'd, a wildcard char '*' will be
- * appended after each single character name, e.g. p-pomes
- * is tried as p* pomes. This has risks as follows: assume
- * Duncan Lawrie sets his alias to "lawrie". A query for
- * d-lawrie will fail as a alias lookup but succeed as a
- * name lookup when written as "d* lawrie". This works until
- * Joe Student sets his alias to "d-lawrie". Whoops.
- * Still in a non-hostile environment, this function may be
- * more useful than dangerous.
- */
- if (equal (*Lpnt, "name")) {
-
- /* Try as is first time for hyphenated names */
- if (NoMore == -1) {
- (void) strcpy (scratch, New->original);
- if (SendQuery (New, *Lpnt, scratch))
- return;
- NoMore = 0;
- }
- else {
- char stemp[MAXSTR], *st = stemp;
-
- for (sp = scratch; *sp != CHNULL; ) {
-
- /* copy until non-space punct char */
- if (!ispunct (*sp) || *sp == ' ' || *sp == '*') {
- #ifdef WILDNAMES
- sp2 = sp;
- #endif /* WILDNAMES */
- *st++ = *sp++;
- if (*sp == CHNULL)
- NoMore++;
- continue;
- }
-
- #ifdef WILDNAMES
- /* if one non-punct char, append * */
- if ((sp - sp2) == 1)
- *st++ = '*';
- #endif /* WILDNAMES */
- *st++ = ' ';
- sp++;
- break;
- }
- while (*sp != CHNULL)
- *st++ = *sp++;
- *st = CHNULL;
- (void) strcpy (scratch, stemp);
- if (SendQuery (New, *Lpnt, scratch))
- return;
- if (NoMore > 0)
- Lpnt++;
- continue;
- }
- }
-
- /*
- * Convert punctuation/separators in scratch to hyphen
- * characters if testing for alias.
- */
- else if (equal (*Lpnt, "alias")) {
- (void) strcpy (scratch, New->original);
- for (sp = scratch; *sp != CHNULL; sp++)
- if (ispunct(*sp))
- *sp = '-';
- if (SendQuery (New, *Lpnt, scratch))
- return;
- Lpnt++;
- }
- else {
- (void) strcpy (scratch, New->original);
- if (SendQuery (New, *Lpnt, scratch))
- return;
- Lpnt++;
- }
- } while (*Lpnt != CPNULL);
- }
- /*
- ** SendQuery -- Send queries to the local CSnet central name server
- **
- ** Takes a field type (alias, call-sign, full name, etc), as known by
- ** the CSnet central name server Query Interpreter, and looks up the
- ** corresponding email address "usercode@host". Cases where the
- ** alias/name aren't found, are ambiguous, or lack an email address
- ** return a message instead of the address. Additional information is
- ** returned as an array of QIR records pointed to by New->QIalt.
- **
- ** Parameters:
- ** New -- pointer to NewAddress struct
- ** Field -- type of field (name, alias, etc) for Value
- ** Value -- name to lookup
- **
- ** Returns:
- ** 1 if a match(es) is found including too many
- ** 0 otherwise
- **
- ** Side Effects:
- ** Will call ContactQI() if the connection is closed.
- ** Modifies contents under New pointer.
- */
-
- SendQuery(New, Field, Value)
- NADD *New;
- const char *Field, *Value;
- {
- QIR *EmailQ, *QIp; /* For handling ReadQI() responses */
- int i; /* good ol' i */
-
- /* Open the ToQI and FromQI descriptors if necessary */
- ContactQI();
-
- /* Make a query out of the arguments */
- fprintf (ToQI,
- "query %s=%s return name alias callsign phone department curriculum email\n",
- Field, Value);
- if (Debug)
- printf ("querying for %s \"%s\"\n", Field, Value);
- if (Log)
- syslog (LOG_DEBUG, "querying for %s \"%s\"\n",
- Field, Value);
- (void) fflush (ToQI);
-
- /*
- * Grab the responses and let the fun begin.
- * The possibilities are:
- *
- * 102:There were N matches to your query
- * -200:1: alias: Paul-Pomes
- * -200:1: name: pomes paul b
- * -200:1: callsign: See Figure 1
- * -508:1: curriculum: Not present in entry.
- * -200:1: department: Computing Services Office
- * -200:1: email: paul@uxc.cso.uiuc.edu
- * 200:Ok.
- *
- * 501:No matches to your query.
- *
- * 502:Too many matches to request.
- */
- EmailQ = ReadQI (FromQI);
-
- /*
- * If we read a preliminary response (99<x<200), garbage
- * collect and read some more.
- */
- i = abs (EmailQ->code);
- if (i > 99 && i < 200) {
- GarbageCollect (EmailQ);
- EmailQ = ReadQI (FromQI);
- }
-
- /*
- * If we read a temporary error, be a nice program and defer.
- */
- else if (i > 399 && i < 500)
- finis ();
-
- /*
- * No matches at all? Too many? Note that single line errors
- * will have code > 0.
- */
- if (EmailQ->code > 0) {
- New->new = CodeString (EmailQ->code);
- New->code = EmailQ->code;
- New->QIalt = QIR_NULL;
- GarbageCollect (EmailQ);
- if (New->code == LR_TOOMANY)
- return (1);
- return (0);
- }
-
- /* anything else must be multi-line */
- assert (EmailQ->code < 0);
-
- /* Are there multiple responses (subcode > 1)? */
- for (QIp = EmailQ; QIp->code < 0; QIp++)
- if (QIp->subcode > 1) {
- New->code = LR_AMBIGUOUS;
- New->new = CodeString (LR_AMBIGUOUS);
- New->QIalt = EmailQ;
- return (1);
- }
-
- /* If one person, handle as single match alias */
- QIp = PickField (EmailQ, EMAIL);
- if (QIp->field != EMAIL) {
- if (Log)
- syslog (LOG_ERR, "Email field for %s (%s) in ph/qi database is present but null",
- Value, Field);
- if (Debug)
- fprintf (stderr, "Email field for %s (%s) in ph/qi database is present but null",
- Value, Field);
- New->code = LR_ABSENT;
- New->new = CodeString (LR_ABSENT);
- return (1);
- }
- New->code = abs (QIp->code);
- New->QIalt = EmailQ;
- switch (abs (QIp->code)) {
- case LR_ABSENT:
- New->new = CodeString (QIp->code);
- return (1);
-
- case LR_OK:
- New->new = QIp->message;
-
- /* chop at first address */
- {
- char *cp = New->new;
-
- while (*cp != CHNULL) {
- if (isspace(*cp) || *cp == ',') {
- *cp == CHNULL;
- break;
- }
- }
- }
- return (1);
-
- default:
- if (Debug)
- fprintf (stderr, "unexpected code %d\n",
- QIp->code);
- if (Log)
- syslog (LOG_ERR, "Query: %s: unexpected code %d", Field, QIp->code);
- finis ();
- }
- GarbageCollect (EmailQ);
- return (0);
- }
- /*
- ** RevQuery -- Reverse query, email to ph alias
- **
- ** Takes a email address as known by the CSnet central name server
- ** Query Interpreter, and looks up the corresponding alias. Cases
- ** where the email address matches multiple aliases return the
- ** original address. In addition the global variable ReplyTo is
- ** set to -1.
- **
- ** Parameters:
- ** New -- pointer to NewAddress struct
- **
- ** Returns:
- ** None
- **
- ** Side Effects:
- ** Will call ContactQI() if the connection is closed.
- ** Modifies contents under New pointer.
- ** ReplyTo set to -1 if QI returns multiple aliases or
- ** no match.
- */
-
- void
- RevQuery(New)
- NADD *New;
- {
- int i;
- QIR *AliasQ, *QIp;
- extern int ReplyTo;
- extern char *From, HostNameBuf[];
- extern FILE *ToQI, *FromQI;
-
- /* Open the ToQI and FromQI descriptors if necessary */
- ContactQI();
-
- /*
- * We have to have a from address here. If it doesn't have
- * a fully qualified form, convert it to name@domain by
- * appending our Fully Qualified Domain Name. FQDN, the
- * litany of the new Internet Age.
- */
-
- assert (From != CPNULL);
- if (index (From, '@') == CPNULL) {
- char *nFrom;
-
- /*
- * We can't Realloc(From) since it may point to
- * an area on the stack.
- */
- nFrom = Malloc ((unsigned)(strlen (From) + 1));
- (void) strcpy (nFrom, From);
- From = Realloc (nFrom, (unsigned)(strlen(nFrom) +
- strlen(HostNameBuf) + 5));
- (void) strcat (From, "@");
- (void) strcat (From, HostNameBuf);
- }
- New->original = From;
-
- /* Send the query
- * I'd check for a -1 here, but am unsure how network errors really
- * are manifested.
- */
- fprintf (ToQI, "query email=%s return alias \n", From);
- if (Debug)
- printf ("querying alias corresponding to \"%s\"\n", From);
- if (Log)
- syslog (LOG_DEBUG, "querying alias for \"%s\"\n", From);
- (void) fflush (ToQI);
-
- /*
- * Grab the responses and let the fun begin.
- * The possibilities are:
- *
- * 102:There was N matches to your query.
- *
- * -200:1: alias: rrv
- * 200:Ok.
- *
- * -200:1: alias: Paul-Pomes
- * -200:2: alias: PostMaster
- * 200:Ok.
- *
- * 501:No matches to your query.
- *
- * 502:Too many matches to request.
- *
- * For anything other than the first case, set ReplyTo to -1 and
- * set New->new = New->original .
- */
- AliasQ = ReadQI (FromQI);
-
- /*
- * If we read a preliminary response (99<x<200), garbage
- * collect and read some more.
- */
- i = abs (AliasQ->code);
- if (i > 99 && i < 200) {
- GarbageCollect (AliasQ);
- AliasQ = ReadQI (FromQI);
- }
-
- /* Handle the 501, 502 codes */
- if (AliasQ->code > 0) {
- ReplyTo = -1;
- New->new = New->original;
- GarbageCollect (AliasQ);
- return;
- }
-
- /* Are there multiple responses (subcode > 1)? */
- for (QIp = AliasQ; QIp->code < 0; QIp++)
- if (QIp->subcode > 1) {
- ReplyTo = -1;
- New->new = New->original;
- GarbageCollect (AliasQ);
- return;
- }
-
- QIp = AliasQ;
- assert (abs (QIp->code) == LR_OK && QIp->field == ALIAS);
- New->code = abs (QIp->code);
- New->new = QIp->message;
- return;
- }
- /*
- ** ReadQI -- Read and store response from QI server
- **
- ** A QI response has one of the following structures:
- **
- ** <-><code>:<subcode><ws><field name>:<string>
- ** 5XX:Error message
- ** 200:Ok.
- **
- ** The leading '-' marks a continuation line. The last line of a
- ** response will not have the '-'.
- **
- ** <code> is the response code. Response codes are listed in phquery.h
- ** and closely follow the conventions of SMTP (RFC-821):
- **
- ** 1XX - status
- ** 2XX - information
- ** 3XX - additional information or action needed
- ** 4XX - temporary errors
- ** 5XX - permanent errors
- ** 6XX - phquery specific codes
- **
- ** <subcode> links multiple fields (e.g., email and pager) to a single
- ** individual. If a name query results in a multiple match, subcode
- ** increments by 1 for each person but has the same value for all response
- ** lines for that individual.
- **
- ** <ws> is sufficient white space to right adjust <field name>: to the
- ** same position on each line.
- **
- ** <field name> is one of the field type in phquery.h (e.g., department,
- ** mailcode, etc).
- **
- ** <string> is either the value for <field name>, if <code> == 200 (LR_OK),
- ** or an error message it <code> is anything else.
- **
- ** Parameters:
- ** InFile - stream pointer for input
- **
- ** Returns:
- ** A pointer to a malloc()'ed block of QI_response structs that
- ** is terminated with QI_response.code > 0.
- **
- ** Side Effects:
- ** Creates a block of data that must be later free()'d.
- ** Advances FromQI.
- */
-
- QIR *
- ReadQI (InFile)
- FILE *InFile;
- {
- int i, code;
- int loopcnt = 1;
- char *tp;
- unsigned size = sizeof (QIR);
- char fstring[MAXSTR]; /* field string */
- char message[MAXSTR]; /* field value */
- char Temp[MAXSTR];
- register QIR *Base, *RepChain;
-
- Base = RepChain = (QIR *) Malloc (size);
- RepChain->field = -1;
- Base->message = CPNULL;
- do {
- *fstring = *message = CHNULL;
- if (fgets (Temp, MAXSTR-1, InFile) == CPNULL) {
- if (Debug)
- fprintf (stderr, "premature EOF\n");
- if (Log)
- syslog (LOG_ERR, "ReadQI: premature EOF");
- finis ();
- }
- if (Debug > 1)
- printf ("ReadQI read =%s=\n", Temp);
- code = atoi (Temp);
-
- /* Positive response codes are formatted "<code>:<message>" */
- if (code > 0) {
- RepChain->subcode = NONE_OF_ABOVE;
- if (sscanf (Temp, "%d:%[^\n]", &RepChain->code, message)
- != 2 || *message == CHNULL) {
- if (Debug)
- fprintf (stderr, "ReadQI: short #1 sscanf\n");
- if (Log)
- syslog (LOG_ERR, "ReadQI: short #1 sscanf read: %m");
- finis ();
- }
- }
-
- /* Otherwise they are the 4 field type */
- else if (( i = sscanf (Temp, "%d:%d:%[^:]: %[^\n]",
- &RepChain->code, &RepChain->subcode, fstring, message))
- != 4 || *fstring == CHNULL || *message == CHNULL) {
- if (Debug)
- fprintf (stderr, "ReadQI: short #2 sscanf, expected 4 got %d\n", i);
- if (Log)
- syslog (LOG_ERR, "ReadQI: short #2 sscanf, expected 4 got %d", i);
-
- /*
- * The short sscanf() read may be due to a embedded
- * newline. If so, continue for a bit to fill out the
- * code field before reading another line.
- */
- if (!(i == 3 && *message == CHNULL))
- finis ();
- }
-
- /*
- * Some fields go over multiple response lines. In that case
- * the field is all blanks. Copy the response field from the
- * previous response if not already set.
- */
- if (RepChain->field == -1) {
- for (tp = fstring; tp <= fstring + (MAXSTR-1) &&
- *tp == ' '; tp++) ;
- if (RepChain->code < 0 && *tp == CHNULL)
- RepChain->field = (RepChain - 1)->field;
- else
- RepChain->field = FieldValue (tp);
- }
-
- /* Now get a new line if message was empty. */
- if (*message == CHNULL)
- continue;
- RepChain->message = Malloc ((unsigned) (strlen (message) + 1));
- (void) strcpy (RepChain->message, message);
- if (RepChain->code > 0)
- break;
- size += sizeof (QIR);
- Base = (QIR *) Realloc ((char *) Base, size);
- RepChain = Base + loopcnt;
- RepChain->field = -1;
- } while (loopcnt++);
- if (Debug)
- for (RepChain = Base; RepChain->code < 0; RepChain++)
- printf ("code %d, subcode %d, field %s, message: %s\n",
- RepChain->code,
- RepChain->subcode,
- Fields[RepChain->field].value,
- RepChain->message);
- return (Base);
- }
- /*
- ** FieldValue -- Locate argument in Fields[] and return integer value
- **
- ** Parameters:
- ** field -- character string to locate in Fields[]
- **
- ** Returns:
- ** integer value of field or NONE_OF_ABOVE (-1) if not found.
- **
- ** Side Effects:
- ** none
- */
-
- FieldValue (field)
- const char *field;
- {
- struct QI_fields *QIp = Fields;
-
- /* Guard against stupid mistakes (so they show up somewhere else?) */
- if (field == CPNULL || *field == CHNULL)
- return (NONE_OF_ABOVE);
-
- /* Replace this with a binary search if profiling peaks here. XXX */
- do {
- if (equal (field, QIp->value))
- break;
- } while ((++QIp)->key != NONE_OF_ABOVE);
- return (QIp->key);
- }
- /*
- ** GarbageCollect -- Free space allocated within QI_response array
- **
- ** Parameters:
- ** QIp -- pointer to array of QI_response
- **
- ** Returns:
- ** None
- **
- ** Side Effects:
- ** none
- */
-
- void
- GarbageCollect (QIp)
- QIR *QIp;
- {
- QIR *QIsave = QIp;
-
- assert (QIp != QIR_NULL);
- do {
- if (QIp->message != CPNULL)
- free (QIp->message);
- QIp->message = CPNULL;
- } while ((QIp++)->code < 0);
- free ((char *) QIsave);
- }
- /*
- ** Malloc -- malloc with error checking
- **
- ** Parameters:
- ** size -- number of bytes to get
- **
- ** Returns:
- ** (char *) of first char of block, or
- ** finis() if any error
- **
- ** Side Effects:
- ** none
- */
-
- char *
- Malloc (size)
- unsigned size; /* Bytes to get */
- {
- char *cp; /* Pointer to memory */
-
- if ((cp = (char *) malloc (size)) == CPNULL) {
- if (Debug) {
- fprintf (stderr, "malloc of %u bytes failed:", size);
- perror("");
- }
- if (Log)
- syslog (LOG_ERR, "malloc of %u bytes failed: %m", size);
- finis ();
- }
- return (cp);
- }
- /*
- ** PrintMsg -- Print a message on the named stream
- **
- ** Parameters:
- ** OutFile -- stream to print message to
- ** Msg - array of char pointers that make up message,
- ** null terminated
- **
- ** Returns:
- ** None
- **
- ** Side Effects:
- ** none
- */
-
- void
- PrintMsg (OutFile, Msg)
- FILE *OutFile;
- char *Msg[];
- {
- while (*Msg != CPNULL) {
- if (fprintf (OutFile, "%s\n", *Msg) < 0)
- finis ();
- Msg++;
- }
- }
- /*
- ** Realloc -- realloc with error checking
- **
- ** Parameters:
- ** ptr -- pointer to existing data
- ** size -- number of bytes to get
- **
- ** Returns:
- ** (char *) of first char of block, or
- ** finis() if any error
- **
- ** Side Effects:
- ** none
- */
-
- char *
- Realloc (ptr, size)
- char *ptr;
- unsigned size;
- {
- char *cp; /* pointer to memory */
-
- if ((cp = (char *) realloc (ptr, size)) == CPNULL) {
- if (Debug) {
- fprintf (stderr, "realloc of %u bytes failed:", size);
- perror("");
- }
- if (Log)
- syslog (LOG_ERR, "realloc of %u bytes failed: %m", size);
- finis ();
- }
- return (cp);
- }
- /*
- ** PrtUsage -- Print how to use message
- **
- ** Print usage messages (char *usage[]) to stderr and exit nonzero.
- ** Each message is followed by a newline.
- **
- ** Parameters:
- ** FullText -- prints the copyright statement if set
- **
- ** Returns:
- ** none
- **
- ** Side Effects:
- ** none
- */
-
- void
- PrtUsage (FullText)
- int FullText;
- {
- int which = 0; /* current line */
-
- while (usage[which] != CPNULL) {
- fprintf (stderr, usage[which++], MyName);
- (void) putc ('\n', stderr);
- }
- (void) fflush (stdout);
- if (FullText)
- PrintMsg (stdout, CopyLeft);
- }
- /*
- ** finis -- Clean up and exit.
- **
- ** Parameters:
- ** none
- **
- ** Returns:
- ** never
- **
- ** Side Effects:
- ** exits sendmail
- */
-
- void
- finis()
- {
- extern FILE *ToQI, *FromQI;
-
- /* clean up temp files */
- if (ToQI != FILE_NULL)
- (void) fclose (ToQI);
- if (FromQI != FILE_NULL)
- (void) fclose (FromQI);
- ToQI = FromQI = FILE_NULL;
-
- if (! Debug) {
- (void) unlink (TmpFile);
- (void) unlink (ErrorFile);
- (void) unlink (NewFile);
- }
-
- /* and exit */
- exit (ExitStat);
- }
-